{
    This file is part of Dev-C++
    Copyright (c) 2004 Bloodshed Software

    Dev-C++ is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Dev-C++ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Dev-C++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
}

unit NewMemberFrm;

interface

uses
    {$IFDEF WIN32}
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, ExtCtrls, CppParser;
{$ENDIF}
{$IFDEF LINUX}
SysUtils, Variants, Classes, QGraphics, QControls, QForms,
QDialogs, QStdCtrls, QExtCtrls, CppParser;
{$ENDIF}

type
    TNewMemberForm = class(TForm)
        Label1: TLabel;
        Label2: TLabel;
        rgScope: TRadioGroup;
        cmbType: TComboBox;
        txtName: TEdit;
        Label3: TLabel;
        txtArguments: TEdit;
        btnCreate: TButton;
        btnCancel: TButton;
        Label4: TLabel;
        cmbClass: TComboBox;
        grpComment: TGroupBox;
        Label5: TLabel;
        Label7: TLabel;
        memDescr: TMemo;
        cmbComment: TComboBox;
        chkToDo: TCheckBox;
        grpAttr: TGroupBox;
        chkStatic: TCheckBox;
        chkVirtual: TCheckBox;
        chkPure: TCheckBox;
        chkInline: TCheckBox;
        procedure cmbTypeChange(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure FormShow(Sender: TObject);
        procedure btnCreateClick(Sender: TObject);
        procedure memDescrChange(Sender: TObject);
        procedure chkStaticClick(Sender: TObject);
    private
        { Private declarations }
        procedure LoadText;
    public
        { Public declarations }
    end;

    //var
    //  NewMemberForm: TNewMemberForm;

implementation

uses
    editor, main, MultiLangSupport, devcfg;

{$R *.dfm}

procedure TNewMemberForm.cmbTypeChange(Sender: TObject);
begin
    btnCreate.Enabled := cmbType.Text <> '';
    btnCreate.Enabled := btnCreate.Enabled and (txtName.Text <> '');
end;

procedure TNewMemberForm.FormClose(Sender: TObject;
    var Action: TCloseAction);
begin
    Action := caFree;
end;

procedure TNewMemberForm.FormShow(Sender: TObject);
var
    sl: TStrings;
begin
    LoadText;
    cmbClass.Text := '';
    cmbType.Text := '';
    txtName.Text := '';
    txtArguments.Text := '';
    chkToDo.Checked := True;
    memDescr.Lines.Clear;
    rgScope.ItemIndex := 2;

    sl := TStringList.Create;
    try
        MainForm.CppParser.GetClassesList(sl);
        cmbClass.Items.Assign(sl);
    finally
        sl.Free;
    end;
    cmbClass.ItemIndex := cmbClass.Items.IndexOf(PStatement(MainForm.ClassBrowser.Selected.Data)^._ScopeCmd);

    cmbType.SetFocus;
end;

procedure TNewMemberForm.btnCreateClick(Sender: TObject);
var
    pID: integer;
    fName: AnsiString;
    CppFname: AnsiString;
    I: integer;
    Line: integer;
    e: TEditor;
    AddScopeStr: boolean;
    S: AnsiString;
    VarName: AnsiString;
    VarType: AnsiString;
    VarArguments: AnsiString;
    VarScope: TStatementClassScope;
    St: PStatement;
    ClsName: AnsiString;
    wa: boolean;
begin
    if cmbClass.ItemIndex = -1 then begin
        MessageDlg(Lang[ID_NEWVAR_MSG_NOCLASS], mtError, [mbOk], 0);
        ModalResult := mrNone;
        Exit;
    end;

    if cmbClass.Items.IndexOf(cmbClass.Text) > -1 then
        St := PStatement(cmbClass.Items.Objects[cmbClass.Items.IndexOf(cmbClass.Text)])
    else
        St := nil;

    if not Assigned(St) then begin
        MessageDlg(Lang[ID_NEWVAR_MSG_NOCLASS], mtError, [mbOk], 0);
        ModalResult := mrNone;
        Exit;
    end;

    with MainForm do begin
        pID := St^._ID;

        VarName := txtName.Text;
        VarType := cmbType.Text;
        case rgScope.ItemIndex of
            0: VarScope := scsPrivate;
            1: VarScope := scsProtected;
            2: VarScope := scsPublic;
            3: VarScope := scsPublished;
        else
            VarScope := scsPublic;
        end;
        VarArguments := txtArguments.Text;
        if Trim(memDescr.Text) = '' then
            memDescr.Text := 'No description';

        CppParser.GetSourcePair(CppParser.GetDeclarationFileName(St), CppFname, fName);

        if not chkInline.Checked and not FileExists(CppFname) then begin
            MessageDlg(Lang[ID_NEWVAR_MSG_NOIMPL], mtError, [mbOk], 0);
            Exit;
        end;

        // if the file is modified, ask to save
        e := GetEditorFromFileName(fName);

        if not Assigned(e) then
            Exit;

        if e.Text.Modified then
            case MessageDlg(format(Lang[ID_MSG_ASKSAVECLOSE], [fName]), mtConfirmation, [mbYes, mbCancel], 0) of
                mrYes: if FileExists(fName) then begin
                        wa := MainForm.devFileMonitor.Active;
                        MainForm.devFileMonitor.Deactivate;
                        e.Text.Lines.SaveToFile(fName);
                        if wa then
                            MainForm.devFileMonitor.Activate;
                    end
                    else
                        Exit;
                mrCancel: Exit;
            end;

        // Ask CppParser for insertion line suggestion ;)
        Line := CppParser.SuggestMemberInsertionLine(pID, VarScope, AddScopeStr);

        if Line = -1 then begin
            // CppParser could not suggest a line for insertion :(
            MessageDlg(Lang[ID_NEWVAR_MSG_NOLINE], mtError, [mbOk], 0);
            Exit;
        end;

        if not Assigned(e) then
            Exit;

        // insert the actual function
        S := #9#9;
        if chkVirtual.Checked then
            S := S + 'virtual '
        else if chkStatic.Checked then
            S := S + 'static ';
        S := S + VarType + ' ' + VarName + '(' + VarArguments + ')';
        if chkPure.Checked then
            S := S + ' = 0L';
        if chkInline.Checked then begin
            e.Text.Lines.Insert(Line, #9#9'}');
            if chkToDo.Checked then
                e.Text.Lines.Insert(Line, #9#9#9'/* TODO (#1#): Implement inline function ' + cmbClass.Text + '::' + VarName + '(' + VarArguments + ') */')
            else
                e.Text.Lines.Insert(Line, #9#9#9'// insert your code here');
            e.Text.Lines.Insert(Line, #9#9'{');
        end
        else
            S := S + ';';

        e.Text.Lines.Insert(Line, S);

        // insert the comment
        if cmbComment.ItemIndex in [0, 1] then // /** ... */ or /* ... */
            e.Text.Lines.Insert(Line, #9#9' */');
        for I := memDescr.Lines.Count - 1 downto 0 do
            if cmbComment.ItemIndex in [0, 1] then // /** ... */ or /* ... */
                e.Text.Lines.Insert(Line, #9#9' * ' + memDescr.Lines[I])
            else
                e.Text.Lines.Insert(Line, #9#9'// ' + memDescr.Lines[I]);
        if cmbComment.ItemIndex = 0 then // /** ... */
            e.Text.Lines.Insert(Line, #9#9'/**')
        else if cmbComment.ItemIndex = 1 then // /* ... */
            e.Text.Lines.Insert(Line, #9#9'/*');

        // insert, if needed, the scope AnsiString
        if AddScopeStr then
            case VarScope of
                scsPrivate: e.Text.Lines.Insert(Line, #9'private:');
                scsProtected: e.Text.Lines.Insert(Line, #9'protected:');
                scsPublic: e.Text.Lines.Insert(Line, #9'public:');
                scsPublished: e.Text.Lines.Insert(Line, #9'published:');
            end;

        e.SetCaretPos(Line + 1, 1);
        e.Text.Modified := True;

        if chkInline.Checked or chkPure.Checked then
            Exit;

        // set the parent class's name
        ClsName := cmbClass.Text;

        e := GetEditorFromFileName(CppFname);
        if not Assigned(e) then
            Exit;

        // insert the implementation
        if Trim(e.Text.Lines[e.Text.Lines.Count - 1]) <> '' then
            e.Text.Lines.Append('');

        // insert the comment
        if cmbComment.ItemIndex = 0 then // /* ... */
            e.Text.Lines.Append('/*');
        for I := 0 to memDescr.Lines.Count - 1 do
            if cmbComment.ItemIndex = 0 then // /* ... */
                e.Text.Lines.Append(' * ' + memDescr.Lines[I])
            else
                e.Text.Lines.Append('// ' + memDescr.Lines[I]);
        if cmbComment.ItemIndex = 0 then // /* ... */
            e.Text.Lines.Append(' */');

        e.Text.Lines.Append(VarType + ' ' + ClsName + '::' + VarName + '(' + VarArguments + ')');
        e.Text.Lines.Append('{');
        if chkToDo.Checked then
            e.Text.Lines.Append(#9'/* TODO (#1#): Implement ' + ClsName + '::' + VarName + '() */')
        else
            e.Text.Lines.Append(#9'// insert your code here');
        e.Text.Lines.Append('}');
        e.Text.Lines.Append('');
        e.SetCaretPos(e.Text.Lines.Count - 1, 1);
        e.Text.Modified := True;
    end;
    Exit;
end;

procedure TNewMemberForm.memDescrChange(Sender: TObject);
begin
    if memDescr.Lines.Count > 1 then
        cmbComment.ItemIndex := 0
    else
        cmbComment.ItemIndex := 2;
end;

procedure TNewMemberForm.LoadText;
begin
    // Set interface font
    Font.Name := devData.InterfaceFont;
    Font.Size := devData.InterfaceFontSize;

    Caption := Lang[ID_POP_NEWMEMBER];
    Label1.Caption := Lang[ID_NEWVAR_VARTYPE];
    Label2.Caption := Lang[ID_NEWMEMB_MEMBNAME];
    Label3.Caption := Lang[ID_NEWMEMB_ARGS];
    Label4.Caption := Lang[ID_NEWVAR_IMPLIN];
    rgScope.Caption := Lang[ID_NEWVAR_SCOPE];
    grpComment.Caption := Lang[ID_NEWVAR_COMMENTS];
    grpAttr.Caption := Lang[ID_NEWMEMB_ATTRS];
    Label5.Caption := Lang[ID_NEWVAR_COMMENTSDESCR];
    Label7.Caption := Lang[ID_NEWVAR_COMMENTSSTYLE];
    chkToDo.Caption := Lang[ID_ADDTODO_MENUITEM];
    btnCreate.Caption := Lang[ID_NEWVAR_BTN_CREATE];
    btnCancel.Caption := Lang[ID_NEWVAR_BTN_CANCEL];
end;

procedure TNewMemberForm.chkStaticClick(Sender: TObject);
begin
    // disable event
    chkStatic.OnClick := nil;
    chkVirtual.OnClick := nil;
    chkPure.OnClick := nil;
    chkInline.OnClick := nil;

    if Sender = chkStatic then begin
        chkInline.Enabled := True;
        if chkStatic.Checked then begin
            chkVirtual.Checked := False;
            chkPure.Checked := False;
        end;
    end
    else if Sender = chkVirtual then begin
        chkInline.Enabled := True;
        if not chkVirtual.Checked then
            chkPure.Checked := False
        else
            chkStatic.Checked := False;
    end
    else if Sender = chkPure then begin
        chkInline.Enabled := not chkPure.Checked;
        chkInline.Checked := False;
        chkStatic.Checked := False;
        chkVirtual.Checked := True;
    end;

    // re-enable event
    chkStatic.OnClick := chkStaticClick;
    chkVirtual.OnClick := chkStaticClick;
    chkPure.OnClick := chkStaticClick;
    chkInline.OnClick := chkStaticClick;
end;

end.
